/*
			// if skeleton data is present, stream skeleton file
			if (m_config.getExportSkeleton()) {
				// open the skeleton.xml output file
				of.open((m_config.getExportPath() + "\\" + m_skeletonFilename).c_str(), std::ios::out);

				// stream the skeleton file
				streamSkeleton(of);
				of.close();
			}

*/

#ifdef 0

	// *******************************************************************************
	// Skeleton streaming functions 
	// *******************************************************************************

	std::string MeshXMLExporter::removeSpaces(const std::string &src) {
		std::string s(src);
		std::string::size_type pos;
		while ((pos=s.find_first_of(" \t\n")) != std::string::npos)
			s.replace(pos, 1, "_");

		return s;
	}

	bool MeshXMLExporter::streamSkeleton(std::ostream &of) {

		// go through and sort out the bone hierarchy (include all of the non-null bones that were not 
		// skinned, as those could still be needed in the application)
		of << "<?xml version=\"1.0\"?>" << std::endl << "<skeleton>" << std::endl;
		of << "\t<bones>" << std::endl;

		// write out the bone rest pose data
		std::map< std::string, int >::const_iterator it = m_boneIndexMap.begin();
		while (it != m_boneIndexMap.end()) {

			INode *thisNode = m_i->GetINodeByName(it->first.c_str());

			of << "\t\t<bone id=\"" << it->second << "\" name=\"" << removeSpaces(it->first) << "\" >" << std::endl;

			// assume rest pose is at time zero
			TimeValue start = m_i->GetAnimRange().Start();
			ObjectState os = thisNode->EvalWorldState(start);
			Object *obj = os.obj;
			SClass_ID scid = obj->SuperClassID();

			// SKELOBJ_CLASS_ID = 0x9125 = 37157
			// BIPED_CLASS_ID = 0x9155 = 37205
			// BIPSLAVE_CONTROL_CLASS_ID = 0x9154 = 37204
			// BIPBODY_CONTROL_CLASS_ID = 0x9156 = 37206
			// FOOTPRINT_CLASS_ID = 0x3011 = 12305
			// DUMMY_CLASS_ID = 0x876234 = 8872500
			Matrix3 tm(thisNode->GetNodeTM(start));
			Matrix3 ptm(thisNode->GetParentTM(start));
			Control *tmc = thisNode->GetTMController();

			TCHAR *nm = thisNode->GetName();
			Class_ID cid = tmc->ClassID();

			if (cid == BIPBODY_CONTROL_CLASS_ID || cid == BIPED_CLASS_ID) {
				if (m_config.getInvertYZ()) {
					Matrix3 m = RotateXMatrix(PI / 2.0f);
					tm = tm * Inverse(m);
				}
			}
			else
				tm = tm * Inverse(ptm);

			Point3 pos = tm.GetTrans();
			AngAxis aa(tm);

			of << "\t\t\t<position x=\"" << pos.x << "\" y=\"" << pos.y << "\" z=\"" << pos.z << "\" />" << std::endl;

			// there is still a lingering Max/Ogre handed-ness issue even after rotating to get the axes correct
			// so we negate the angle of rotation here
			of << "\t\t\t<rotation angle=\"" << -aa.angle << "\">" << std::endl;
			of << "\t\t\t\t<axis x=\"" << aa.axis.x << "\" y=\"" << aa.axis.y << "\" z=\"" << aa.axis.z << "\" />" << std::endl;
			of << "\t\t\t</rotation>" << std::endl;
			of << "\t\t</bone>" << std::endl;

			it++;
		}

		of << "\t</bones>" << std::endl;

		// write out the bone hierarchy
		it = m_boneIndexMap.begin();
		of << "\t<bonehierarchy>" << std::endl;
		while (it != m_boneIndexMap.end()) {
			INode *thisNode = m_i->GetINodeByName(it->first.c_str());

			if (thisNode != 0) {
				INode *parentNode = thisNode->GetParentNode();

				if (parentNode != 0 && parentNode != m_i->GetRootNode())
					of << "\t\t<boneparent bone=\"" << removeSpaces(it->first) << "\" parent=\"" << removeSpaces(std::string(parentNode->GetName())) << "\"/>" << std::endl;
			}

			it++;
		}
		of << "\t</bonehierarchy>" << std::endl;

		// the fun bits....
		// Animations are named by the user during export; Max has no concept of animation subset names, 
		// so we have to get the user to do that manually. If the user has entered anything for animations,
		// spit it all out here.
		std::list<NamedAnimation>::iterator anim = m_animations.begin();

		if (anim != m_animations.end()) {
			of << "\t<animations>" << std::endl;

			while (anim != m_animations.end()) {

				NamedAnimation a = *anim;
				anim++;

				float fps = (float)GetFrameRate();
				float length = (a.end - a.start) / fps;

				of << "\t\t<animation name=\"" << removeSpaces(a.name) << "\" length=\"" << length << "\">" << std::endl;

				streamAnimTracks(of, a.start, a.end);

				of << "\t\t</animation>" << std::endl;
			}

			of << "\t</animations>" << std::endl;
		}

		of << "</skeleton>" << std::endl;

		return true;
	}

	static int _compare_func(const void *a, const void *b) { return *(( int *)a) - *(( int *)b); }

	bool MeshXMLExporter::streamAnimTracks(std::ostream &of, int startFrame, int endFrame) {

		int start = startFrame * GetTicksPerFrame();
		int end = endFrame * GetTicksPerFrame();

		std::map< std::string, int >::const_iterator it = m_boneIndexMap.begin();

		of << "\t\t\t<tracks>" << std::endl;

		// need this for calculating keyframe values
		Matrix3 initTM, bipedMasterTM0;
		IBipMaster *bip = 0;
		bipedMasterTM0.IdentityMatrix();

		while (it != m_boneIndexMap.end()) {

			INode *thisNode = m_i->GetINodeByName(it->first.c_str());
			it++;

			Control *c = thisNode->GetTMController();
			Class_ID cid = c->ClassID(); 

			Tab<TimeValue> keyTimes;
			Interval interval(start, end);

			/*
			-- gets initial transform at frame 0f
			at time 0f (
				initTform = d.transform ;
				if (not isRootUniversal2 d) then (
					mparent = d.parent.transform ;
					initTform = initTform*inverse(mparent) ;
				)
				else if (flipYZ) then (
					if (not g_MAX) then
						format " - flipping root track..." ;
					-- we add the bip Transform
					--initTform = initTform * d.controller.rootNode.transform ;
					initTform = flipYZTransform initTform ;
				)
			)
			*/

			initTM = thisNode->GetNodeTM(0);

			// must have at least a frame at the start...
			keyTimes.Append(1, &start);

			TCHAR *tch = thisNode->GetName();

			// SKELOBJ_CLASS_ID = 0x9125 = 37157
			// BIPED_CLASS_ID = 0x9155 = 37205
			// BIPSLAVE_CONTROL_CLASS_ID = 0x9154 = 37204
			// BIPBODY_CONTROL_CLASS_ID = 0x9156 = 37206
			// FOOTPRINT_CLASS_ID = 0x3011 = 12305
			// DUMMY_CLASS_ID = 0x876234 = 8872500

			// three-part controller for Biped root -- taking this cue from the old MaxScript exporter code
			if (cid == BIPBODY_CONTROL_CLASS_ID) {

				// we deal with the initial transform as-is, except that it might need to
				// be rotated (since the root transform is in world coords)
				if (m_config.getInvertYZ())
					initTM = initTM * Inverse(RotateXMatrix(PI/2.0f));

				if (cid == BIPBODY_CONTROL_CLASS_ID) {
					// get the keys from the horiz, vert and turn controllers
					bip = GetBipMasterInterface(c);
					Control *biph = bip->GetHorizontalControl();
					Control *bipv = bip->GetVerticalControl();
					Control *bipr = bip->GetTurnControl();

					biph->GetKeyTimes(keyTimes, interval, KEYAT_POSITION | KEYAT_ROTATION);
					bipv->GetKeyTimes(keyTimes, interval, KEYAT_POSITION | KEYAT_ROTATION);
					bipr->GetKeyTimes(keyTimes, interval, KEYAT_POSITION | KEYAT_ROTATION);
				}
			}
			else if (cid == BIPSLAVE_CONTROL_CLASS_ID) {
				// slaves just have keys, apparently
				c->GetKeyTimes(keyTimes, interval, KEYAT_POSITION | KEYAT_ROTATION);
			
				// put initial transform into local coordinates -- since this is relative to the
				// parent, we don't need to sweat that possible rotations here
				initTM = initTM * Inverse(thisNode->GetParentTM(0));
			}

			// ...and stick a frame at the end as well...it will get sorted out if it is redundant
			keyTimes.Append(1, &end);

			// skip redundant key times here
			keyTimes.Sort(_compare_func);

	//		if (cid == BIPSLAVE_CONTROL_CLASS_ID || cid == BIPBODY_CONTROL_CLASS_ID || cid == FOOTPRINT_CLASS_ID) {
	//		
	//			if (cid == BIPBODY_CONTROL_CLASS_ID) {
	//				initTM = thisNode->GetNodeTM(0);
	//
	//				if (m_flipYZ)
	//					initTM = initTM * RotateXMatrix(PI/2.0f);
	//				bipedMasterTM0 = initTM;
	//			}
	//			else
	//				initTM = bipedMasterTM0;
	//
	//			streamBipedKeyframes(of, bip, thisNode, keyTimes, interval, initTM);
	//		}
	//		else
				streamKeyframes(of, thisNode, keyTimes, interval, initTM);
		}

		of << "\t\t\t</tracks>" << std::endl;

		return true;
	}

	bool MeshXMLExporter::streamKeyframes(std::ostream &of, INode *thisNode, Tab<TimeValue> &keyTimes, Interval &interval, Matrix3 &initTM) {

		of << "\t\t\t\t<track bone=\"" << removeSpaces(std::string(thisNode->GetName())) << "\">" << std::endl;
		of << "\t\t\t\t\t<keyframes>" << std::endl;

		int i;
		int keyTime = -1;
		int start = interval.Start();
		int end = interval.End();

		/*
		
		-- gets initial transform at frame 0f
		at time 0f (
			initTform = d.transform ;
			if (not isRootUniversal2 d) then (
				mparent = d.parent.transform ;
				initTform = initTform*inverse(mparent) ;
			)
			else if (flipYZ) then (
				if (not g_MAX) then
					format " - flipping root track..." ;
				-- we add the bip Transform
				--initTform = initTform * d.controller.rootNode.transform ;
				initTform = flipYZTransform initTform ;
			)
		)
		*/
		initTM = thisNode->GetNodeTM(0);

		Control *c = thisNode->GetTMController();
		Control *pc = thisNode->GetParentNode()->GetTMController();
		bool isRoot = false;

		if (c > 0)
			if (c->ClassID() == BIPBODY_CONTROL_CLASS_ID)
				isRoot = true;
	//	if (pc > 0)
	//		if (pc->ClassID() == BIPBODY_CONTROL_CLASS_ID || pc->ClassID() == FOOTPRINT_CLASS_ID)
	//			isRoot = true;

		TCHAR *tc = thisNode->GetName();
		if (!isRoot) {
			Matrix3 ptm = thisNode->GetParentTM(0);
			initTM = initTM * Inverse(ptm);
		}
		else if (m_config.getInvertYZ()) {
			initTM = initTM * Inverse(RotateXMatrix(PI/2.0f));
		}

		for (i=0; i<keyTimes.Count(); i++) {
				
			// only operate within the supplied keyframe time range
			if (keyTimes[i] < start)
				continue;
			if (keyTimes[i] > end)
				break;

			// ignore key times we've already processed
			if (keyTimes[i] != keyTime) {

				keyTime = keyTimes[i];
				float keyTimef = (float) (keyTimes[i] - start) / (float)GetTicksPerFrame() / (float)GetFrameRate();

				of << "\t\t\t\t\t\t<keyframe time=\"" << keyTimef << "\">" << std::endl;

				/*

				function flipYZTransform Tform = (
					local axis1,axis2,axis3,t,m
					
					-- computes the matrix
					axis1 = point3 1 0 0 ;
					axis2 = point3 0 0 1 ;
					axis3 = point3 0 -1 0 ;
					t = point3 0 0 0 ;
					m=matrix3 axis1 axis2 axis3 t ;
					
					-- multiplies by the inverse
					Tform = Tform*inverse(m) ;

					return Tform ;
				)


				-- First, rotation which depends on initial transformation
				Tform = d.transform ;
				*/
				Matrix3 tm = thisNode->GetNodeTM(keyTime);

				/*
				-- if this is the pelvis
				if (isRootUniversal2 d) then (
					mparent = matrix3 1 ;

					if (flipYZ) then
						Tform = flipYZTransform Tform ;
				)			
				else
					mparent = d.parent.transform ; 
				*/

				// if this node's parent's controller is the biped controller, then this is either Pelvis or Footsteps,
				// and both should be treated as root nodes

				Matrix3 ident;
				Matrix3 ptm;
				ident.IdentityMatrix();
				Control *tmc = thisNode->GetTMController();
				TCHAR *tc = thisNode->GetName();

				if (tmc->ClassID() == BIPBODY_CONTROL_CLASS_ID) {

					ptm = ident;
					if (m_config.getInvertYZ()) {
						tm = tm * Inverse(RotateXMatrix(PI/2.0f));
					}
				}
				else
					ptm = thisNode->GetParentNode()->GetNodeTM(keyTime);

				/*


				-- computes rotation
				mref = initTform*mparent ;	
				Tform = Tform*inverse(mref) ;
				*/

				Matrix3 mref = initTM * ptm;
				tm = tm * Inverse(mref);

				/*
				
				-- rotation part is saved.
				rot = toAngleAxis Tform.rotation ;
				axis = rot.axis;
				angle = - rot.angle;
				*/

				AngAxis aa(tm);

				/*
				-- Then, position which depends on parent			
				Tform=d.transform ;
				Tform=Tform*inverse(mparent) ;

				*/

				tm = thisNode->GetNodeTM(keyTime) * Inverse(ptm);

				/*

				-- if this is the root bone and flipYZ == true
				if (isRootUniversal2 d and flipYZ) then (
					Tform = flipYZTransform Tform ;
				)

				*/

				if (m_config.getInvertYZ() && thisNode->GetParentNode()->GetParentTM(0).IsIdentity()) {
					tm = tm * Inverse(RotateXMatrix(PI/2.0f));
				}

				/*
				-- substracts position of the initial transform
				Tform.pos -= initTform.pos ;
				Tform.pos = Tform.pos * scale ;
				
				pos = Tform.pos ;
				*/
				Point3 trans = tm.GetTrans();
				trans -= initTM.GetTrans();

				of << "\t\t\t\t\t\t\t<translate x=\"" << trans.x << "\" y=\"" << trans.y << "\" z=\"" << trans.z << "\" />" << std::endl;
				of << "\t\t\t\t\t\t\t<rotate angle=\"" << -aa.angle << "\">" << std::endl;
				of << "\t\t\t\t\t\t\t\t<axis x=\"" << aa.axis.x << "\" y=\"" << aa.axis.y << "\" z=\"" << aa.axis.z << "\" />" << std::endl;
				of << "\t\t\t\t\t\t\t</rotate>" << std::endl;

				of << "\t\t\t\t\t\t</keyframe>" << std::endl;
			}
		}

		of << "\t\t\t\t\t</keyframes>" << std::endl;
		of << "\t\t\t\t</track>" << std::endl;

		return true;
	}

	bool MeshXMLExporter::streamBipedKeyframes(std::ostream &of, IBipMaster *bip, INode *thisNode, Tab<TimeValue> &keyTimes, Interval &interval, Matrix3 &initTM) {

		of << "\t\t\t\t<track bone=\"" << removeSpaces(std::string(thisNode->GetName())) << "\">" << std::endl;
		of << "\t\t\t\t\t<keyframes>" << std::endl;

		int i;
		int keyTime = -1;
		int start = interval.Start();
		int end = interval.End();
		Matrix3 tm(thisNode->GetNodeTM(start));
		Matrix3 ptm(thisNode->GetParentTM(start));

		for (i=0; i<keyTimes.Count(); i++) {
				
			// only operate within the supplied keyframe time range
			if (keyTimes[i] < start)
				continue;
			if (keyTimes[i] > end)
				break;

			// ignore key times we've already processed
			if (keyTimes[i] != keyTime) {

				keyTime = keyTimes[i];
				float keyTimef = (float) (keyTimes[i] - start) / (float)GetTicksPerFrame() / (float)GetFrameRate();

				of << "\t\t\t\t\t\t<keyframe time=\"" << keyTimef << "\">" << std::endl;

				Control *tmc = thisNode->GetTMController();

				TCHAR *nm = thisNode->GetName();
				Class_ID cid = tmc->ClassID();

				if (cid == BIPBODY_CONTROL_CLASS_ID || cid == BIPED_CLASS_ID) {
					if (m_config.getInvertYZ()) {
						Matrix3 m = RotateXMatrix(PI / 2.0f);
						tm = tm * Inverse(m);
					}
				}
				else
					tm = tm * Inverse(ptm);

				//Point3 trans = bip->GetBipedPos(keyTime, thisNode);
				//Quat q = bip->GetBipedRot(keyTime, thisNode);

				Point3 trans = tm.GetTrans();
				trans = trans * Inverse(initTM);
				trans -= initTM.GetTrans();

				//AngAxis aa(q);
				AngAxis aa(tm);
				float ang = aa.angle;
				Point3 axis = aa.axis;
	        
				of << "\t\t\t\t\t\t\t<translate x=\"" << trans.x << "\" y=\"" << trans.y << "\" z=\"" << trans.z << "\" />" << std::endl;
				of << "\t\t\t\t\t\t\t<rotate angle=\"" << -ang << "\">" << std::endl;
				of << "\t\t\t\t\t\t\t\t<axis x=\"" << axis.x << "\" y=\"" << axis.y << "\" z=\"" << axis.z << "\" />" << std::endl;
				of << "\t\t\t\t\t\t\t</rotate>" << std::endl;

				of << "\t\t\t\t\t\t</keyframe>" << std::endl;
			}
		}

		of << "\t\t\t\t\t</keyframes>" << std::endl;
		of << "\t\t\t\t</track>" << std::endl;

		return true;
	}

			
			
#endif